linux网络编程(三) |
您所在的位置:网站首页 › linux bind unbind › linux网络编程(三) |
今天我们来看bind()函数,函数原型如下: #include int bind(int sockfd, const struct sockaddr *address, socklen_t address_len);该函数的功能给socket绑定一个地址,这样client对这个地址的相应收发数据就能和socket相关联 服务端: 必须要调用bind进行绑定 客户端: 非必须调用,如不调用,则系统自动分配一个端口和本地地址来进行和socket绑定 函数参数sockfd:socket文件描述符 address:构造一个ip+端口的地址,结构体类型为structsockaddr address_len:地址参数的长度sizeof(address) 返回值:成功返回0,失败返回-1, 并且设置errno 这里address参数值的一说: struct sockaddr { sa_family_t sa_family; /* address family, AF_xxx */ char sa_data[14]; /* 14 bytes of protocol address */ };这是早期的协议地址类型,后面出现了IPV4、IPV6,又定义了新的sockaddr //ipv4 struct sockaddr_in { __kernel_sa_family_t sin_family; /* Address family */ __be16 sin_port; /* Port number */ struct in_addr sin_addr; /* Internet address */ /* Pad to size of `struct sockaddr'. */ unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) - sizeof(unsigned short int) - sizeof(struct in_addr)]; }; /* Internet address. */ struct in_addr { __be32 s_addr; }; //ipv6 struct sockaddr_in6 { unsigned short int sin6_family; /* AF_INET6 */ __be16 sin6_port; /* Transport layer port # */ __be32 sin6_flowinfo; /* IPv6 flow information */ struct in6_addr sin6_addr; /* IPv6 address */ __u32 sin6_scope_id; /* scope id (new in RFC2553) */ }; //unix addr #define UNIX_PATH_MAX 108 struct sockaddr_un { __kernel_sa_family_t sun_family; /* AF_UNIX */ char sun_path[UNIX_PATH_MAX]; };比如绑定一个ipv4地址: #include #include #include #include #include #include int main(int argc, char **argv) { int fd = socket(AF_INET, SOCK_STREAM, 0); printf("fd:%d\n", fd); struct sockaddr_in servaddr; bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(8080); int ret = bind(fd, (struct sockaddr*)&servaddr, sizeof(servaddr)); if (ret = 0) { // SELInux相关的处理 err = security_socket_bind(sock, (struct sockaddr *)&address, addrlen); if (!err) // 如果是SOCK_STREAM的话调用的是inet_bind() err = sock->ops->bind(sock, (struct sockaddr *) &address, addrlen); } fput_light(sock->file, fput_needed); } return err; }通过fd找到socket实例 static struct socket *sockfd_lookup_light(int fd, int *err, int *fput_needed) { // 从当前task_struct->files中找到fd实例 struct fd f = fdget(fd); struct socket *sock; *err = -EBADF; if (f.file) { // 通过file找到socket实例 sock = sock_from_file(f.file, err); if (likely(sock)) { *fput_needed = f.flags & FDPUT_FPUT; return sock; } fdput(f); } return NULL; }bind的实现func: const struct proto_ops inet_stream_ops = { .family = PF_INET, .owner = THIS_MODULE, .release = inet_release, .bind = inet_bind, // 这就是bind的实现func .connect = inet_stream_connect, .socketpair = sock_no_socketpair, .accept = inet_accept, .getname = inet_getname, ...... };该func内部其实就是对sock->sk进行一些赋值 int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) { struct sock *sk = sock->sk; int err; /* If the socket has its own bind function then use it. (RAW) */ if (sk->sk_prot->bind) { return sk->sk_prot->bind(sk, uaddr, addr_len); } if (addr_len sk_net会被赋值 unsigned short snum; // 端口 int chk_addr_ret; // 地址类型: 单播 多播 广播 u32 tb_id = RT_TABLE_LOCAL; int err; // 非inet协议簇 if (addr->sin_family != AF_INET) { /* Compatibility games : accept AF_UNSPEC (mapped to AF_INET) * only if s_addr is INADDR_ANY. */ err = -EAFNOSUPPORT; if (addr->sin_family != AF_UNSPEC || addr->sin_addr.s_addr != htonl(INADDR_ANY)) goto out; } // 查找地址类型 tb_id = l3mdev_fib_table_by_index(net, sk->sk_bound_dev_if) ? : tb_id; chk_addr_ret = inet_addr_type_table(net, addr->sin_addr.s_addr, tb_id); /* Not specified by any standard per-se, however it breaks too * many applications when removed. It is unfortunate since * allowing applications to make a non-local bind solves * several problems with systems using dynamic addressing. * (ie. your servers still start up even if your ISDN link * is temporarily down) */ err = -EADDRNOTAVAIL; if (!inet_can_nonlocal_bind(net, inet) && addr->sin_addr.s_addr != htonl(INADDR_ANY) && chk_addr_ret != RTN_LOCAL && chk_addr_ret != RTN_MULTICAST && chk_addr_ret != RTN_BROADCAST) goto out; snum = ntohs(addr->sin_port); // 绑定的端口进行字节序转换 err = -EACCES; if (snum && snum user_ns, CAP_NET_BIND_SERVICE)) goto out; /* We keep a pair of addresses. rcv_saddr is the one * used by hash lookups, and saddr is used for transmit. * * In the BSD API these are the same except where it * would be illegal to use them (multicast/broadcast) in * which case the sending device address is used. */ if (with_lock) lock_sock(sk); // 如果状态不是初始的TCP_CLOSE则报错 /* Check these errors (active socket, double bind). */ err = -EINVAL; if (sk->sk_state != TCP_CLOSE || inet->inet_num) goto out_release_sock; inet->inet_rcv_saddr = inet->inet_saddr = addr->sin_addr.s_addr; if (chk_addr_ret == RTN_MULTICAST || chk_addr_ret == RTN_BROADCAST) inet->inet_saddr = 0; /* Use device */ // 如果没有传端口,则随机选择一个端口,注意:0~1024需要特权 /* Make sure we are allowed to bind here. */ if (snum || !(inet->bind_address_no_port || force_bind_address_no_port)) { if (sk->sk_prot->get_port(sk, snum)) { inet->inet_saddr = inet->inet_rcv_saddr = 0; err = -EADDRINUSE; goto out_release_sock; } err = BPF_CGROUP_RUN_PROG_INET4_POST_BIND(sk); if (err) { inet->inet_saddr = inet->inet_rcv_saddr = 0; goto out_release_sock; } } if (inet->inet_rcv_saddr) sk->sk_userlocks |= SOCK_BINDADDR_LOCK; if (snum) sk->sk_userlocks |= SOCK_BINDPORT_LOCK; inet->inet_sport = htons(inet->inet_num); inet->inet_daddr = 0; inet->inet_dport = 0; sk_dst_reset(sk); err = 0; out_release_sock: if (with_lock) release_sock(sk); out: return err; }总结bind函数内部做的事其实很单一,主要如下: 1.通过fd找到对应的socket实例 2.进行(地址+端口)的参数校验 3.对socket实例中的成员赋值 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |